#pragma once
#include <core/container/hash_prime.h>
#include <core/strings/simple_array.h>
#include <core/container/StringPtrMap.h>

template<int CATEGORY, int CAPCITY>
class TinyStringMap{
	// 使用哈希表
	typedef unsigned short ushort;
	typedef TinyStringMap<CATEGORY,CAPCITY> ThisType;
	struct Key {
		ushort		data;
	public:
		Key() :data(0){}
		Key(ushort d) :data(d){}
		inline operator short()const{ return data; }
		inline bool operator==(const Key& d)const{ return data == d.data; }
		inline unsigned int GetHash()const{ return (unsigned int)data; }
	};
	struct Text{
		Text*	hashprev;			// 哈西插槽的双链表的后一个
		Text*	hashnext;			// 哈西插槽的双链表的前一个
		Key		key;				// 用于标志
		char*	ptr;				// 实际字符串
		int		count;				// 引用次数
		Text() :hashprev(0), hashnext(0), ptr(0), count(0){}
	};
	typedef qb::base::KeyHashMap<Text, Key> TextHashMap;
	//typedef std::map<std::string, ushort> ReverseMap;
	typedef qb::base::StringPtrMap  ReverseMap;
	class TextEraser : public TextHashMap::Listener{
	public:
		virtual void OnEntry(Text* entry, int slot){
			if (entry){
				if (entry->ptr)	delete[] entry->ptr;
				delete entry;
			}
		}
	};
	class TextArray{
		qb::base::SimpleArray<Text*>	m_pointers;
	public:
		TextArray(){}
		~TextArray(){}
		void Enum(TextEraser* listener){
			for (int i = 0; i < (int)m_pointers.size(); i++)
			{
				Text* txt = m_pointers[i];
				if (listener && txt)
					listener->OnEntry(txt, i);
			}
		}
		int GetBytes()const{
			return m_pointers.size()*sizeof(Text)+
				m_pointers.getcapcity()*sizeof(Text*);
		}
		Text* Find(unsigned int key, unsigned int nhash)const
		{
			if (key >= 0 && key < (unsigned int)m_pointers.size())
				return m_pointers[key];
			return nullptr;
		}
		void Insert(Text* entry, unsigned int nhash){
			m_pointers.push_back(entry);
		}
		void Expand(int capcity){ m_pointers.setcapcity(capcity); }
	};
	typedef TextArray ValueStorage;
	//typedef TextHashMap ValueStorage;
	ValueStorage		m_text_map;
	ReverseMap			m_reverse_map;
	char*				m_blank;
	ushort				m_next_key;
	int					m_memory_bytes;
	int					m_category;
	int					m_capcity;
	std::mutex			m_lock;//TODO:自旋锁
public:
	static ThisType& Get(){
		static ThisType instance;
		return instance;
	}
	TinyStringMap() :m_next_key(1), m_category(CATEGORY), m_capcity(CAPCITY),
		m_reverse_map(128)
	{
		m_memory_bytes = sizeof(ThisType) - sizeof(m_text_map) + 16;
		m_capcity = qb::base::GetHashPrimeLargerThan(m_capcity*2);
		m_text_map.Expand(m_capcity);
		m_text_map.Insert(0, 0);
		m_blank = new char[16];
		memset(m_blank, 0, 16);
		//BVIEW("TinyStringMap instance 0x:%8x  CATEGORY:%d CAPCITY:%d hashmap:%d", this, CATEGORY, CAPCITY, m_capcity);
	}
	~TinyStringMap()
	{
		std::lock_guard<std::mutex> locer(m_lock);
		TextEraser eraser;
		m_text_map.Enum(&eraser);
		if (m_blank)delete[] m_blank;
	}
	int Memory(){
		std::lock_guard<std::mutex> locer(m_lock);
		int bytes = m_memory_bytes + m_text_map.GetBytes();
		bytes += m_reverse_map.GetBytes();
		return bytes;
	}
	int Category()const{ return CATEGORY; }
	const char* Load(const ushort& key)
	{
		std::lock_guard<std::mutex> locker(m_lock);
		Text* ptext = m_text_map.Find(key, (unsigned int)key);
		if (ptext)
			return ptext->ptr;
		return m_blank;
	}
	ushort Save(const char* str,int len=0)
	{
		if (str == NULL || *str == '\0')
			return 0;
		if (len<=0)
			len = str ? strlen(str) : 0;
		if (len <= 0)
			return 0;
		
		std::lock_guard<std::mutex> locker(m_lock);
		void* ptr = nullptr;
		if (m_reverse_map.Lookup(str, ptr))
		{
			uint64_t ulkey = (uint64_t)ptr;
			ushort ukey = (ushort)ulkey;//TODO: x64?
			//Key key(ukey);
			Text* ptext = m_text_map.Find(ukey, (unsigned int)ukey);
			//TODO BASSERTS(ptext, "key:%d  str:%s", ukey, str);
			if (ptext)
			{
				ptext->count++;
				return ukey;
			}
		}
		return AddString(str,len);
	}
protected:
	ushort AddString(const char* str, int len)
	{
		ushort key = m_next_key++;
		//m_reverse_map[strkey] = key;
		m_reverse_map.SetAt(str, (void*)key);
		Text* ptext = new Text();
		ptext->key = key;
		ptext->count = 1;
		ptext->ptr = new char[len + 1];
		memcpy(ptext->ptr, str, len);
		ptext->ptr[len] = 0;
		m_text_map.Insert(ptext, (unsigned int)key);
		m_memory_bytes += sizeof(Text) + len + 1;
		return key;
	}

};
